/*
 * Copyright 2012-2014 glodon paas All right reserved. This software is the
 * confidential and proprietary information of glodon paas ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with glodon paas.
 */
package com.glodon.paas.framework.ibatis;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.glodon.paas.framework.exception.QuerySizeExceedLimitLoggingException;

/**
 * ibatis客户端拦截器 用cglib的方式做增强
 * 
 * @author licx 2013-1-31 下午3:17:29
 */
public class SqlMapClientInterceptor implements MethodInterceptor {

	private static final Logger	logger		= LoggerFactory.getLogger(SqlMapClientInterceptor.class);

	// cglib的代理器
	private Enhancer			enhancer	= new Enhancer();

	// 被代理的对象实例
	private Object				object;

	private static final int	LOG_QUERY_SIZE_LIMIT;

	static {
		int iLimit = 10000;
		String limit = System.getProperty("sqlmap.log.query.size.limit");
		try {
			if (StringUtils.isNotBlank(limit)) {
				iLimit = Integer.parseInt(limit);
			}
		} catch (Exception e) {
			// ignore exception
		}
		LOG_QUERY_SIZE_LIMIT = iLimit;
	}

	/**
	 * 默认的拦截方法
	 * 
	 * @param o
	 * @param method
	 * @param args
	 * @param proxy
	 * @return
	 * @throws Throwable
	 */
	public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		Object result = null;

		// 执行调用
		String methodName = method.getName();
		// 拦截指定的方法
		if (methodName.startsWith("query") || methodName.startsWith("update") || methodName.startsWith("insert")) {

			long startTime = System.nanoTime();

			result = proxy.invoke(object, args);

			// 取得Statement
			String statementName = "Unknown";
			if (args.length > 0 && args[0] instanceof String) {
				statementName = (String) args[0];
			}

			// 添加监控信息
			logger.info("intercept sql statementName:" + statementName + ",nanoTime:"
					+ (int) (System.nanoTime() - startTime));

		} else {
			result = proxy.invoke(object, args);
		}

		// 从数据库查询批量数据并且返回的是List结果
		try {
			if (methodName.startsWith("query") && (result != null) && (result instanceof List)) {
				// 如果返回的结果超过限制数量则写异常日志
				if (((List<?>) result).size() > LOG_QUERY_SIZE_LIMIT) {
					StringBuilder sb = new StringBuilder(100);
					if (args != null) {
						for (Object arg : args) {
							sb.append("  ---- ");
							if (arg == null) {
								sb.append("<null>");
							} else if ((arg instanceof String) || (arg instanceof Number)
									|| (arg instanceof Collection<?>) || (arg instanceof Map<?, ?>)) {
								sb.append(arg.getClass().getSimpleName()).append("[").append(arg).append("]");
							} else {
								sb.append(ToStringBuilder.reflectionToString(arg, ToStringStyle.SHORT_PREFIX_STYLE));
							}
							sb.append("\n");
						}
					}
					logger.error("Query from database exceed limit, with args:\n" + sb.toString(),
							new QuerySizeExceedLimitLoggingException("Query list has " + ((List<?>) result).size()
									+ " records, limit is " + LOG_QUERY_SIZE_LIMIT + "."));
				}
			}
		} catch (Exception e) {
			// ignore exception
		}
		return result;
	}

	/**
	 * 构造一个代理的实例.
	 * 
	 * @param clz
	 * @param object
	 * @return
	 */
	public Object proxy(Object object) {
		this.object = object;
		enhancer.setSuperclass(object.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

}
